﻿// <copyright file="StcEthernetBroker.cs" company="Hallvard Korsgaard">
//  Microsoft Public License (Ms-PL). See license details here: http://enoceannet.codeplex.com/license
// </copyright>
// <author>Hallvard Korsgaard</author>

namespace EnOceanService
{
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Net;
  using System.Text;
  using EnOcean.Radio.Sensor;
  using StcEthernet;

  /// <summary>
  /// This class is responsible for keeping track of STC-Ethernet devices.
  /// </summary>
  public static class StcEthernetBroker
  {
    /// <summary>
    /// Used for keeping track of the transievers so we can clean up before exiting.
    /// </summary>
    private static readonly List<Transiever> transievers = new List<Transiever>();

    /// <summary>
    /// The server that receives events form the STC-Ethernet transiever.
    /// </summary>
    private static Server server;

    /// <summary>
    /// Gets or sets the event log.
    /// </summary>
    internal static EventLog EventLog
    {
      get;
      set;
    }

    /// <summary>
    /// Starts listening for connections from STC-Ethernet devices.
    /// </summary>
    internal static void Start()
    {
      IPAddress serverAddress;
      IPEndPoint serverEndpoint;

      // Create a new server listening on the configured TCP port.
      serverAddress = IPAddress.Parse(Properties.Settings.Default.ServerIPAddress);
      serverEndpoint = new IPEndPoint(serverAddress, Properties.Settings.Default.ServerPort);
      server = new Server(serverEndpoint);

      // Hook up the event handlers.
      server.TransieverConnected += new EventHandler<TransieverConnectedEventArgs>(TransieverConnected);
      server.TransieverInitialized += new EventHandler<TransieverInitializedEventArgs>(TransieverInitialized);

      // Start to accept connections.
      server.Accept();
    }

    /// <summary>
    /// Shuts down the server.
    /// </summary>
    internal static void Stop()
    {
      // Stop the server
      server.Close();
      server = null;

      // Close Transiever objects
      lock (transievers)
      {
        foreach (Transiever t in transievers)
        {
          t.Close();
        }

        transievers.Clear();
      }
    }

    /// <summary>
    /// Returns the base id's of the connected transievers.
    /// </summary>
    /// <returns>The base id's of the connected transievers.</returns>
    internal static IEnumerable<long> GetBaseIds()
    {
      List<long> ids;

      ids = new List<long>();

      lock (transievers)
      {
        foreach (Transiever t in transievers)
        {
          ids.Add(t.BaseId);
        }
      }

      return ids;
    }

    /// <summary>
    /// Sends a SensorTelegram.
    /// </summary>
    /// <param name="telegram">The SensorTelegram to send.</param>
    internal static void Send(SensorTelegram telegram)
    {
      Transiever tran = null;
      
      lock (transievers)
      {
        foreach (Transiever t in transievers)
        {
          if (t.BaseId == (telegram.Id & 0xFFFFFF80))
          {
            tran = t;
            break;
          }
        }
      }

      if (tran != null)
      {
        tran.Send(telegram);
      }
      else
      {
        throw new ArgumentOutOfRangeException("No transievers matched the given sensor ID.");
      }
    }

    /// <summary>
    /// This event handler will be called when a transiever connects.
    /// The event handler code must be thread safe because several events can fire at the same time.
    /// </summary>
    /// <param name="sender">The object that sent the event.</param>
    /// <param name="e">The event data.</param>
    private static void TransieverConnected(object sender, TransieverConnectedEventArgs e)
    {
      EventLog.WriteEntry("Transiever connected.", EventLogEntryType.Information);

      lock (transievers)
      {
        transievers.Add(e.Transiever);
      }
    }

    /// <summary>
    /// This event handler will be called when the Transiever object is ready for use.
    /// The event handler code must be thread safe because several events can fire at the same time.
    /// </summary>
    /// <param name="sender">The object that sent the event.</param>
    /// <param name="e">The event data.</param>
    private static void TransieverInitialized(object sender, TransieverInitializedEventArgs e)
    {
      StringBuilder sb;

      sb = new StringBuilder();

      sb.AppendLine("Transiever initialized.");
      sb.AppendLine("Base ID: " + e.Transiever.BaseId.ToString("X8"));
      sb.AppendLine("Modem on: " + e.Transiever.IsModemOn);
      sb.AppendLine("Modem ID: " + e.Transiever.ModemId.ToString("X4"));
      sb.AppendLine("Operating mode: " + e.Transiever.OperatingMode);
      sb.AppendLine("Radio sensitivity: " + e.Transiever.RadioSensitivity);
      sb.AppendLine("Software version: " + e.Transiever.SoftwareVersion);

      EventLog.WriteEntry(sb.ToString(), EventLogEntryType.Information);

      // Hooking up event handlers.
      e.Transiever.TelegramReceived += new EventHandler<TelegramReceivedEventArgs>(ReceiverBroker.Transiever_TelegramReceived);
      e.Transiever.TransieverDisconnected += new EventHandler<TransieverDisconnectedEventArgs>(Transiever_TransieverDisconnected);
    }

    /// <summary>
    /// This event handler will be called when a transiever disconnects.
    /// The event handler code must be thread safe because several events can fire at the same time.
    /// </summary>
    /// <param name="sender">The object that sent the event.</param>
    /// <param name="e">The event data.</param>
    private static void Transiever_TransieverDisconnected(object sender, TransieverDisconnectedEventArgs e)
    {
      EventLog.WriteEntry("Transiever disconnected.");

      lock (transievers)
      {
        transievers.Remove(e.Transiever);
      }
    }
  }
}
